﻿#region Imports
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using CommonUtilities.Enums;
using CommonUtilities.Extensions;
using CommonUtilities.Logging;
using CommonUtilities.Messages;
using CommonUtilities.Models;
using CommonUtilities.Util;
using DirectoryAnalyzer.Controls.ViewModels;
using DirectoryAnalyzer.Enumerations;
using DirectoryAnalyzer.Events;
using DirectoryAnalyzer.Main;
using DirectoryAnalyzer.Models;
using DirectoryAnalyzer.ResourceFiles;
#endregion

namespace DirectoryAnalyzer.Views
{
    public partial class frmSpaceAnalyzer : Form
    {
        // TEST - RKD
        public static string TestPath
        {
            get
            {
                string path;
                //path = @"H:\Dropbox\DirectoryInfo\DirectoryInfo";
                //path = @"P:\[wallps]";
                //path = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Application.ExecutablePath))) + @"\_Test";
                path = @"Z:\DOWNLAODs";
                path = @"Z:\DOWNLOADs\SEASONs";
                path = @"C:\Documents and Settings\rhythm divine\My Documents";
                path = @"Z:\Dropbox\`NCR";
                path = @"E:\[Nikon d7000]\";
                path = @"E:\[Nikon D7000]\20170526-30 Iceland\";

                return path;
            }
        }

        #region Declarations
        bool isDataLoaded = false;
        string sBasePath = String.Empty;
        string sTotalSize = "Total size";
        long totDirSize = 0;
        int iTimeElapsed = 0;
        int maxAllowed = 5;
        int offset = 2;
        double allowedForClubbing = 0.1;
        internal const string TEXT_FOR_FILES = "..";
        string displayForFiles = "Misc files";
        SeriesChartType defaultChartType = SeriesChartType.Column;
        bool supressCloseConfirmation = false;

        ObservableCollection<FileCategory> allFileCategories = new ObservableCollection<FileCategory>();
        ObservableCollection<DInfo> allDirInfo = new ObservableCollection<DInfo>();
        private int legendTrimLength = 20;
        #endregion

        #region Properties
        public ObservableCollection<string> AllStatus { get; set; }

        public Dictionary<string, Dictionary<eAnalysisMode, ObservableCollection<object>>> AnalysisHistory { get; set; }

        public Dictionary<int, string> PathHistory { get; set; }

        public SeriesChartType LastChartType { get; private set; }

        public eAnalysisMode AnalysisMode
        {
            get
            {
                eAnalysisMode? analysisMode = null;

                if (radSubDirectories.Checked)
                { analysisMode = eAnalysisMode.SubDirectory; }
                else if (radFileTypes.Checked)
                { analysisMode = eAnalysisMode.FileType; }

                return analysisMode.Value;
            }
        }
        #endregion

        #region Constructors
        public frmSpaceAnalyzer()
        {
            InitializeComponent();
            this.btnSortDirectioner = new CommonUtilities.Controls.CUtil_SortDirectioner(false);
            ValidateFileTypeCheckbox(radSubDirectories.Checked ? radSubDirectories : radFileTypes);

            InitializeControlData();
            AnalysisHistory = new Dictionary<string, Dictionary<eAnalysisMode, ObservableCollection<object>>>();
            PathHistory = new Dictionary<int, string>();
            cmbPathHistory.ValueMember = "Key";
            cmbPathHistory.DisplayMember = "Value";
            cmbPathHistory.DataSource = PathHistory.ToObservableCollection();
            cmbPathHistory.DroppedDown = false;

            cmbChartType.PopulateComboBoxWithEnum<SeriesChartType>(selection: defaultChartType);

            pnlChart.Location = dgrdDirectoryInfo.Location.AddToLocation(offset / 2, offset / 2);
            pnlChart.Size = dgrdDirectoryInfo.Size.AddToSize(-offset, -offset);
            lstStatus.DrawMode = DrawMode.OwnerDrawVariable;
            ToggleButtonViewModel.textFalse = "Show chart";
            ToggleButtonViewModel.textTrue = "Hide chart";
            ToggleButtonViewModel.isToggleChecked = false;
            tglLoadChart.ToggleStateChangedEventHandler += tglLoadChart_ToggleStateChanged;
            tglLoadChart.IsEnabled = false;
            tglLoadChart.UpdateCheckState(false);
            if (Debugger.IsAttached) tglLoadChart.UpdateGraphImage("pack://application:,,,/DirectoryAnalyzer;component/Images/pie.png"); // RKD
            UpdateStatusBox();
            UpdateColor();
            UpdateMode(true);
            txtPath.Focus();

            // TEST - RKD
            txtPath.Text = TestPath;
            txtPath_Leave(null, null);
        }
        #endregion

        #region Events
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlgBrowseFolder = new FolderBrowserDialog
            {
                ShowNewFolderButton = true,
                Description = "Select folder to analyze..",
                RootFolder = Environment.SpecialFolder.MyComputer
            };

            dlgBrowseFolder.SelectedPath = Directory.Exists(sBasePath) ? sBasePath
                : Environment.SpecialFolder.MyComputer.GetFolderPath();
            dlgBrowseFolder.ShowDialog(this);
            txtPath.Text = dlgBrowseFolder.SelectedPath;
        }

        private void UpdateStatus(string msg, bool isMarker = false, bool clear = false)
        {
            if (AllStatus.IsNull()) clear = true;
            if (clear) AllStatus = new ObservableCollection<string>();

            AllStatus.Add(DateTime.Now.ToString("MM-dd HH:mm:ss") + " --> " + (isMarker ? "-=-=-=-=-=- " + msg.TrimEnd('.') + " -=-=-=-=-=-" : msg));

            lstStatus.DataSource = new ObservableCollection<string>(AllStatus);
            lstStatus.SelectedItem = AllStatus.Last();
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            UpdateMode(true);

            DialogResult? selectedLoadOption = null;
            if (AnalysisHistory._ContainsKey(sBasePath))
            {
                if (AnalysisHistory[sBasePath]._ContainsKey(AnalysisMode))
                {
                    selectedLoadOption = MessageBoxUtil.Show(Program.IsDebug, ResourceDirectoryAnalyzer.Msg_DirectoryAnalysisReload,
                        ResourceDirectoryAnalyzer.App_DirectoryAnalysisReload, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Question);

                    if (selectedLoadOption == DialogResult.Abort) return;
                    else if (selectedLoadOption == DialogResult.Retry)
                    {
                        switch (AnalysisMode)
                        {
                            case eAnalysisMode.FileType:
                                allFileCategories = AnalysisHistory[sBasePath][AnalysisMode].Cast<FileCategory>().ToObservableCollection();
                                break;

                            case eAnalysisMode.SubDirectory:
                            default:
                                allDirInfo = AnalysisHistory[sBasePath][AnalysisMode].Cast<DInfo>().ToObservableCollection();
                                break;
                        }

                        InitDGAndUpdateSortOptions();
                    }
                }
                else selectedLoadOption = DialogResult.Ignore;
            }
            else selectedLoadOption = DialogResult.Ignore;

            #region Clean-up
            if (selectedLoadOption == DialogResult.Ignore)
            { allDirInfo = new ObservableCollection<DInfo>(); allFileCategories = new ObservableCollection<FileCategory>(); }
            #endregion

            isDataLoaded = true;
            DateTime startTime = DateTime.Now;
            iTimeElapsed = 0;
            btnMinMax.Text = "";
            tmrMinimizeStatus.Stop();

            ToggleStatusBox(true);
            UpdateStatus(ResourceDirectoryAnalyzer.Status_Path.FormatTextFromParams(sBasePath));
            UpdateStatus(ResourceDirectoryAnalyzer.Status_ValidatingPath, true);

            long totFileCount = 0;
            totDirSize = 0;
            if (Directory.Exists(sBasePath))
            {
                UpdateStatus(ResourceDirectoryAnalyzer.Status_PathValid);

                UpdateStatus(ResourceDirectoryAnalyzer.Status_CleaningData);
                InitializeControlData();

                long dirSize = 0;
                long fileSize = 0;
                bool isWarning = false;

                #region Show by Sub-directories
                if (AnalysisMode == eAnalysisMode.SubDirectory)
                {
                    if (selectedLoadOption == DialogResult.Ignore)
                    {
                        // Looking for files in sub-directories
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LookingInSubDirectories, true);
                        ObservableCollection<string> allDirectories
                            = new ObservableCollection<string>(Directory.GetDirectories(sBasePath));

                        if (allDirectories.GetCount() > 0)
                        {
                            UpdateStatus(ResourceDirectoryAnalyzer.Status_SubDirectoriesFound.FormatTextFromParams(allDirectories.GetCount()));
                            allDirectories.ToList().ForEach(dir =>
                                {
                                    try
                                    {
                                        allDirInfo.Add(new DInfo(dir, 1));
                                    }
                                    catch (Exception ex)
                                    {
                                        isWarning = true;
                                        UpdateStatus(ResourceDirectoryAnalyzer.Status_SkippingDirectory.FormatTextFromParams(
                                            new DirectoryInfo(dir).FullName.Replace(sBasePath.Trim(Path.DirectorySeparatorChar), ".."),
                                            ex.Message.Replace(dir.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, "~")));
                                    }
                                });

                            dirSize = allDirInfo.Select(file => file.ByteSize).Sum();
                            UpdateStatus(ResourceDirectoryAnalyzer.Status_FilesFoundInSubDirecto.FormatTextFromParams(allDirInfo.Select(dir => dir.FileCount).Sum()));
                        }
                        else UpdateStatus(ResourceDirectoryAnalyzer.Status_NoSubDirectories);

                        // Looking for files outside sub-directories
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LookingOutsideSubDirectories, true);
                        ObservableCollection<string> files
                            = new ObservableCollection<string>(Directory.GetFiles(sBasePath));

                        ObservableCollection<FInfo> allFileInfo = new ObservableCollection<FInfo>();
                        if (files.GetCount() > 0)
                        {
                            files.ToList().ForEach(file => allFileInfo.Add(new FInfo(file, true)));
                            fileSize = allFileInfo.Select(file => file.ByteSize).Sum();

                            UpdateStatus(ResourceDirectoryAnalyzer.Status_FilesFound.FormatTextFromParams(files.GetCount()));
                            allDirInfo.Add(new DInfo(TEXT_FOR_FILES, fileSize, 0, files.GetCount()));
                        }
                        else UpdateStatus(ResourceDirectoryAnalyzer.Status_NoFilesFound);
                    }
                    else if (selectedLoadOption == DialogResult.Retry)
                    {
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LoadingPrevAnalysis);
                        dirSize = allDirInfo.Select(dir => dir.ByteSize).Sum();
                    }

                    totDirSize = dirSize + fileSize;
                    totFileCount = allDirInfo.Select(info => info.FileCount).Sum();
                    if (selectedLoadOption == DialogResult.Ignore) allDirInfo.Add(new DInfo(sTotalSize, totDirSize, allDirInfo.GetCount(), totFileCount));

                    PopulateGrid(allDirInfo);
                }
                #endregion

                #region Show by File types
                if (AnalysisMode == eAnalysisMode.FileType)
                {
                    if (selectedLoadOption == DialogResult.Ignore)
                    {
                        // Looking for files in sub-directories
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LookingInSubDirectories, true);
                        ObservableCollection<string> allDirectories = new ObservableCollection<string>(Directory.GetDirectories(sBasePath));

                        ObservableCollection<string> allFilePaths = new ObservableCollection<string>();
                        if (allDirectories.GetCount() > 0)
                        {
                            UpdateStatus(ResourceDirectoryAnalyzer.Status_SubDirectoriesFound.FormatTextFromParams(allDirectories.GetCount()));
                            allDirectories.ToList().ForEach(dir =>
                            {
                                try
                                {
                                    Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories).ToList().ForEach(file => allFilePaths.Add(file));
                                }
                                catch (Exception ex)
                                {
                                    isWarning = true;
                                    UpdateStatus(ResourceDirectoryAnalyzer.Status_SkippingDirectory.FormatTextFromParams(
                                        new DirectoryInfo(dir).FullName.Replace(sBasePath.Trim(Path.DirectorySeparatorChar), ".."),
                                        ex.Message.Replace(dir.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, "~")));
                                }
                            });

                            UpdateStatus(ResourceDirectoryAnalyzer.Status_FilesFoundInSubDirecto.FormatTextFromParams(allFilePaths.GetCount()));
                        }
                        else UpdateStatus(ResourceDirectoryAnalyzer.Status_NoSubDirectories);

                        // Looking for files outside sub-directories
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LookingOutsideSubDirectories, true);
                        long dirFileCount = allFilePaths.GetCount();
                        Directory.GetFiles(sBasePath).ToList().ForEach(file => allFilePaths.Add(file));
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_FilesFound.FormatTextFromParams((allFilePaths.GetCount() - dirFileCount)));

                        UpdateStatus(ResourceDirectoryAnalyzer.Status_Analyzing_Files, true);

                        // Extn
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_AccumulatingFiles);
                        ObservableCollection<FileInfo> allFiles = new ObservableCollection<FileInfo>();
                        allFilePaths.ToList().ForEach(file => allFiles.Add(new FileInfo(file)));

                        UpdateStatus(ResourceDirectoryAnalyzer.Status_CollectingExtensions);
                        ObservableCollection<KeyValueCount> extnSummary = new ObservableCollection<KeyValueCount>();
                        foreach (string extn in allFiles.GroupBy(file => file.Extension).Select(file => file.First().Extension))
                        {
                            ObservableCollection<FileInfo> files = new ObservableCollection<FileInfo>(allFiles.Where(file => file.Extension == extn));
                            extnSummary.Add(new KeyValueCount(extn.Trim('.').ToUpper(), files.Select(file => file.GetLength()).Sum(), files.Count()));
                        }

                        UpdateStatus(ResourceDirectoryAnalyzer.Status_CategorizingSummary);
                        var settings = FileTypes.AllFileTypes;
                        ObservableCollection<KeyValueCount> categorySummary = new ObservableCollection<KeyValueCount>();
                        foreach (string extn in extnSummary.Select(smry => smry.Key))
                        {
                            string ctg = settings.Where(ftype => ftype.Value._Contains(extn)).Select(ftype => ftype.Key).FirstOrDefault();
                            if (ctg == null) ctg = (chkUnknowFileTypes.Checked ? extn : "Misc") + " files"; // Reload this view by clubbing unknow file types as Misc
                            if (!categorySummary.Select(smry => smry.Key)._Contains(ctg)) categorySummary.Add(new KeyValueCount(ctg, 0, 0));

                            KeyValueCount category = categorySummary.Where(ctgr => ctgr.Key == ctg).First();
                            category.Value += extnSummary.Where(smry => smry.Key == extn).Select(smry => smry.Value).First();
                            category.Count += extnSummary.Where(smry => smry.Key == extn).Select(smry => smry.Count).First();
                        }

                        totFileCount = categorySummary.Select(ctg => ctg.Count).Sum();
                        categorySummary.ToList().ForEach(ctg => allFileCategories.Add(new FileCategory(ctg.Key, ctg.Value, 1, ctg.Count)));
                    }
                    else if (selectedLoadOption == DialogResult.Retry)
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_LoadingPrevAnalysis);

                    totDirSize = allFileCategories.Select(smry => smry.ByteSize).Sum();
                    allFileCategories.Add(new FileCategory(sTotalSize, totDirSize, allFileCategories.GetCount(), allFileCategories.Select(smry => smry.FileCount).Sum()));

                    PopulateGrid(allFileCategories);

                    #region Get file types
                    /* // RKD
                    ObservableCollection<string> allFileExtn = new ObservableCollection<string>();
                    foreach (DriveInfo drive in DriveInfo.GetDrives()) //  Environment.GetLogicalDrives();
                    {
                        try
                        {
                            ObservableCollection<string> allFilePaths = new ObservableCollection<string>();
                            ObservableCollection<string> allDirectories = new ObservableCollection<string>(Directory.GetDirectories(drive.Name));

                            // Directories
                            allDirectories.ToList().ForEach(dir =>
                                {
                                    try
                                    {
                                        //allFilePaths.ToList().AddRange(Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories));
                                        Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories).ToList().ForEach(file => allFilePaths.Add(file));
                                    }
                                    catch (Exception ex)
                                    {
                                        ;
                                    }
                                });

                            // Files
                            Directory.GetFiles(drive.Name).ToList().ForEach(file => allFilePaths.Add(file));

                            // Extn
                            allFilePaths.Select(file => Path.GetExtension(file)).Distinct().ToList().ForEach(ext => allFileExtn.Add(ext));

                            //Console.WriteLine("Drive Type: {0}", drive.DriveType);
                            //Console.WriteLine("Drive Size: {0}", drive.TotalSize);
                            //Console.WriteLine("Drive Free Space: {0}", drive.TotalFreeSpace);
                        }
                        catch (Exception ex)
                        {
                            ;
                        }
                    }

                    string allext = "";
                    allFileExtn.Distinct().OrderBy(ext => ext).ToList().ForEach(ext => allext += ext.Trim('.') + "\n");
                    Clipboard.SetText(allext);
                    */
                    #endregion
                }
                #endregion Show by File types

                UpdateStatusBox();

                #region Available disk space
                bool isWholeVolume = (new DirectoryInfo(sBasePath).Root.FullName == sBasePath);
                long avlblSpace = 0; long totalSpace = 0;

                DriveInfo driveInfo = new DriveInfo(sBasePath);
                avlblSpace = driveInfo.AvailableFreeSpace;
                totalSpace = driveInfo.TotalSize;

                long spc = isWholeVolume ? avlblSpace : totDirSize;
                progSizeOccupied.Value = Convert.ToInt32((1 - (spc * 1.0 / totalSpace)) * 100);
                lblUsedSize.Text = progSizeOccupied.Value + "%";
                #endregion Available disk space

                #region Completion summary
                DateTime completionTime = DateTime.Now;
                UpdateStatus(ResourceDirectoryAnalyzer.Status_AccumulatingSummary, true);

                MemoryMgmt mem = new MemoryMgmt(eByteValueType.B, totDirSize);
                MemoryMgmt memAvlbl = new MemoryMgmt(eByteValueType.B, avlblSpace);
                MemoryMgmt memTotal = new MemoryMgmt(eByteValueType.B, totalSpace);

                UpdateStatus(ResourceDirectoryAnalyzer.Status_TotalSize.FormatTextFromParams(mem.PracticalSize));
                lblTotalSize.Text = String.Format("Total {0}", mem.PracticalSize);
                UpdateStatus(ResourceDirectoryAnalyzer.Status_AnalysisCompleted.FormatTextFromParams(Utilities.GetDuration(startTime, completionTime)));
                lblDuration.Text = String.Format("COMPLETED in {0}", Utilities.GetDuration(startTime, completionTime));
                lblFilesCount.Text = String.Format("{0} files", totFileCount); // display nFiles in statusbar
                lblAvgSize.Text = String.Format("{0} / file", new MemoryMgmt(eByteValueType.B, totDirSize * 1.0 / totFileCount).PracticalSize);

                if (isWholeVolume) UpdateStatus(ResourceDirectoryAnalyzer.Status_DiskSpaceAvailable.FormatTextFromParams(memAvlbl.PracticalSize, memTotal.PracticalSize));
                MessageBoxUtil.Show(Program.IsDebug, "Total size\t: {0}".FormatTextFromParams(mem.PracticalSize) + "\t".Repeator(3)
                              + "\nFile count\t: " + lblFilesCount.Text
                              + "\nAvg. size\t: " + lblAvgSize.Text
                              + "\n\nDetails:"
                              + mem.SummaryByteTypeValue(eByteValueType.B)
                              + mem.SummaryByteTypeValue(eByteValueType.KB)
                              + mem.SummaryByteTypeValue(eByteValueType.MB)
                              + mem.SummaryByteTypeValue(eByteValueType.GB)
                              + mem.SummaryByteTypeValue(eByteValueType.TB)
                              + (isWholeVolume ? Utilities.cNewLine.Repeator(2) + ResourceDirectoryAnalyzer.Status_DiskSpaceAvailable.FormatTextFromParams(memAvlbl.PracticalSize, memTotal.PracticalSize) : ""),
                         ResourceDirectoryAnalyzer.App_Summary, MessageBoxButtons.OK, MessageBoxIcon.Information
                    );
                #endregion

                #region Add to Analysis/Path History
                if (selectedLoadOption == DialogResult.Ignore)
                {
                    UpdateStatus(ResourceDirectoryAnalyzer.Status_UpdatingHistory, true);
                    UpdateStatus(ResourceDirectoryAnalyzer.Status_AddingAnalysisToHistory);
                    Dictionary<eAnalysisMode, ObservableCollection<object>> dict = null;
                    if (!AnalysisHistory.TryGetValue(sBasePath, out dict))
                        AnalysisHistory.AddToDictionary(sBasePath, dict);

                    ObservableCollection<object> valueAnalysis = null;
                    switch (AnalysisMode)
                    {
                        case eAnalysisMode.FileType:
                            valueAnalysis = allFileCategories.ToObservableCollection<object>();
                            break;

                        case eAnalysisMode.SubDirectory:
                        default:
                            valueAnalysis = allDirInfo.ToObservableCollection<object>();
                            break;
                    }

                    dict = dict.AddToDictionary(AnalysisMode, valueAnalysis);
                    AnalysisHistory[sBasePath] = dict;

                    UpdateStatus(ResourceDirectoryAnalyzer.Status_AddingPathToHistory);
                    IsUpdatingPathHistory = true;
                    bool ignoreStringCase = true;
                    if (PathHistory._ContainsValue(sBasePath, ignoreStringCase))
                        PathHistory.Remove(PathHistory.GetKeyFromValue(sBasePath, ignoreStringCase));
                    PathHistory.AddToDictionary(PathHistory.LastOrDefault().Key + 1, sBasePath);
                    cmbPathHistory.DataSource = PathHistory._OrderByDescending(path => path.Key)._Select(path => path).ToObservableCollection(); // new { Key = path.Key, Value = path.Value }
                    cmbPathHistory.SelectedItem = sBasePath;
                    IsUpdatingPathHistory = false;
                }
                #endregion

                #region Auto-hide status update
                iTimeElapsed = 0;
                tmrMinimizeStatus.Start();
                tglLoadChart.IsEnabled = true;
                btnMinMax.Text = (maxAllowed - iTimeElapsed).ToString();
                #endregion

                UpdateStatus(ResourceDirectoryAnalyzer.Status_LoggingAnalysisDetails, true);
                WriteToActivityLog(lstStatus.Items._Cast<string>(), startTime, completionTime, null, isWarning);

                UpdateStatus(ResourceDirectoryAnalyzer.Status_BatchComplete, true);
            }
            else UpdateStatus(ResourceDirectoryAnalyzer.Status_PathInvalid);
        }

        private void chkUnknowFileTypes_CheckedChanged(object sender, EventArgs e)
        {
            // RKD
        }

        /// <summary>
        /// Generates DataGridView columns and populates Sorting options
        /// </summary>
        private void InitDGAndUpdateSortOptions()
        {
            dgrdDirectoryInfo.Enabled = btnSortDirectioner.Enabled = cmbSortProperty.Enabled = false;
            ObservableCollection<IdValueMember<string, string>> sortOptions = new ObservableCollection<IdValueMember<string, string>>();

            switch (AnalysisMode)
            {
                case eAnalysisMode.FileType:
                    FileCategory fcatg = new FileCategory();
                    dgrdDirectoryInfo.GenerateDGVColumns<FileCategory>(true);
                    cmbSortProperty.PopulateSortableProperties<FileCategory>(dgrdDirectoryInfo);

                    // RKD : DELETE
                    //fcatg.GetType().GetProperties().ForEach(prop =>
                    //{
                    //    bool isSortable = fcatg.GetPropertyAttributeProperty<FileCategory, bool>(prop.Name, "SortableAttribute", "Sortable", false);
                    //    string displayText = fcatg.GetPropertyAttributeProperty<FileCategory, string>(prop.Name, "DisplayNameAttribute", "DisplayName", prop.Name);
                    //
                    //    if (isSortable && dgrdDirectoryInfo.Columns["col" + prop.Name] != null)
                    //        sortOptions.Add(new IdValueMember(prop.Name, displayText));
                    //});
                    break;

                case eAnalysisMode.SubDirectory:
                default:
                    DInfo dinfo = new DInfo();
                    dgrdDirectoryInfo.GenerateDGVColumns<DInfo>(true);
                    cmbSortProperty.PopulateSortableProperties<DInfo>(dgrdDirectoryInfo);

                    // RKD : DELETE
                    //dinfo.GetType().GetProperties().ForEach(prop =>
                    //{
                    //    bool isSortable = dinfo.GetPropertyAttributeProperty<DInfo, bool>(prop.Name, "SortableAttribute", "Sortable", false);
                    //    string displayText = dinfo.GetPropertyAttributeProperty<DInfo, string>(prop.Name, "DisplayNameAttribute", "DisplayName", prop.Name);
                    //
                    //    if (isSortable && dgrdDirectoryInfo.Columns["col" + prop.Name] != null)
                    //        sortOptions.Add(new IdValueMember(prop.Name, displayText));
                    //});
                    break;
            }

            cmbSortProperty.SelectedValue = "ByteSize";
            ClearGrid();
            dgrdDirectoryInfo.Enabled = btnSortDirectioner.Enabled = cmbSortProperty.Enabled = true;
        }

        private void txtPath_TextChanged(object sender, EventArgs e)
        {
            sBasePath = AddressFormatEvent._TextChanged(sender);
        }

        private void chkMax_CheckedChanged(object sender, EventArgs e)
        {
            UpdateStatusBox();
        }

        private void btmMinMax_Click(object sender, EventArgs e)
        {
            ToggleStatusBox(null);
        }

        private void ToggleStatusBox(bool? value)
        {
            chkMax.Checked = value.HasValue ? value.Value : !chkMax.Checked;
            UpdateColor();
        }

        private void frmDirectoryInfo_Resize(object sender, EventArgs e)
        {
            FormResizeEvent._FormResize(sender, e, this);
            UpdateStatusBox();
        }

        private void txtPath_Leave(object sender, EventArgs e)
        {
            AddressLeaveEvent._TextLeave(sender, sBasePath);
            if (!cmbPathHistory.Focused) cmbPathHistory.DroppedDown = false;
        }

        private void lstStatus_MeasureItem(object sender, MeasureItemEventArgs e)
        {
            int countNewLine = (int)Utilities.GetLineCount(((ListBox)sender).Items[e.Index]);
            e.ItemHeight = countNewLine * lstStatus.ItemHeight;
        }

        private void lstStatus_DrawItem(object sender, DrawItemEventArgs e)
        {
            e.DrawBackground();
            if (e.Index > -1)
                e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(), e.Font, new SolidBrush(e.ForeColor), e.Bounds);
        }

        public void tmrMinimizeStatus_Tick(object sender, EventArgs e)
        {
            iTimeElapsed++;
            btnMinMax.Text = (maxAllowed - iTimeElapsed).ToString();
            if (iTimeElapsed >= maxAllowed) { ToggleStatusBox(false); tmrMinimizeStatus.Stop(); btnMinMax.Text = ""; }
        }

        private void Timer_Stop(object sender, EventArgs e)
        {
            tmrMinimizeStatus.Stop();
            btnMinMax.Text = "";
        }

        private void cmnuCopyDataWithHeaders_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(SelectData(dgrdDirectoryInfo.SelectedRows, true));
            UpdateStatus(ResourceDirectoryAnalyzer.Status_GridDataCopied.FormatTextFromParams("with"));
        }

        private void cmnuCopyData_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(SelectData(dgrdDirectoryInfo.SelectedRows));
            UpdateStatus(ResourceDirectoryAnalyzer.Status_GridDataCopied.FormatTextFromParams("w/o"));
        }

        private void cmnuSelectAll_Click(object sender, EventArgs e)
        {
            dgrdDirectoryInfo.SelectAll();
            UpdateStatus(ResourceDirectoryAnalyzer.Status_AllDataSelected);
        }

        private void tglLoadChart_ToggleStateChanged(object sender, EventArgs e)
        {
            pnlChart.Visible = tglLoadChart.GetCheckedState();

            if (isDataLoaded && tglLoadChart.GetCheckedState())
            {
                isDataLoaded = false;
                UpdateStatus(ResourceDirectoryAnalyzer.Status_DrawingChart, true);
                chrtSpaceAnalysis.Series.ToList().ForEach(col => col.Points.Clear());

                switch (AnalysisMode)
                {
                    case eAnalysisMode.FileType:
                        #region Show by File types
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_ConstructingGraph);

                        allFileCategories.Where(info => info.ByteSize >= totDirSize * allowedForClubbing && info.FileType != sTotalSize).ToList()
                            .ForEach(info => chrtSpaceAnalysis.Series["Space"].Points.AddXY(info.FileType.TrimToSize(legendTrimLength, true) + " (" + info.ByteSize.ToPracticalSize() + ")", info.ByteSize));

                        if (allFileCategories.Where(info => info.ByteSize < totDirSize * allowedForClubbing).Any())
                        {
                            long smallSegments = allFileCategories.Where(info => info.ByteSize < totDirSize * allowedForClubbing).Select(info => info.ByteSize).Sum();
                            chrtSpaceAnalysis.Series["Space"].Points.AddXY("Others" + " (" + smallSegments.ToPracticalSize() + ")", smallSegments);
                        }

                        UpdateStatus(ResourceDirectoryAnalyzer.Status_DisplayingGraph);
                        #endregion
                        break;

                    case eAnalysisMode.SubDirectory:
                    default:
                        #region Show by Sub-directories
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_ConstructingGraph);

                        long borderValue = Convert.ToInt64(totDirSize * allowedForClubbing);
                        allDirInfo.OrderByDescending(info => info.ByteSize).Where(info => info.ByteSize >= borderValue && info.DirName != sTotalSize).ToList()
                            .ForEach(info => chrtSpaceAnalysis.Series["Space"].Points.AddXY(info.DirName.TrimToSize(legendTrimLength, true) + " (" + info.ByteSize.ToPracticalSize() + ")", info.ByteSize));
                        allDirInfo.OrderByDescending(info => info.ByteSize).Where(info => info.ByteSize >= borderValue && info.DirName != sTotalSize).ToList()
                            .ForEach(info => chrtSpaceAnalysis.Series["Average"].Points.AddXY(info.DirName.TrimToSize(legendTrimLength, true) + " (" + info.ByteSize.ToPracticalSize() + ")", info.AvgByteSize));

                        if (allDirInfo.Where(info => info.ByteSize < totDirSize * allowedForClubbing).Any())
                        {
                            long count = allDirInfo.Where(info => info.ByteSize < borderValue).GetCount();
                            long smallSegments = allDirInfo.Where(info => info.ByteSize < borderValue).Select(info => info.ByteSize).Sum();
                            chrtSpaceAnalysis.Series["Space"].Points.AddXY("Others" + " (" + smallSegments.ToPracticalSize() + ")", smallSegments);
                            chrtSpaceAnalysis.Series["Average"].Points.AddXY("Others" + " (" + smallSegments.ToPracticalSize() + ")", smallSegments / count);
                        }

                        chrtSpaceAnalysis.Series["Space"].Points.ToList()
                            .ConvertAll(pts => pts.AxisLabel = pts.AxisLabel.StartsWith(TEXT_FOR_FILES) ? pts.AxisLabel.Replace(TEXT_FOR_FILES, displayForFiles) : pts.AxisLabel);
                        UpdateStatus(ResourceDirectoryAnalyzer.Status_DisplayingGraph);
                        #endregion
                        break;
                }

                // Format Y-axis as PracticalSize // RKD
                LegendCollection legcoll = chrtSpaceAnalysis.Legends;
            }
        }

        private void ChartType_Changed(object sender, EventArgs e)
        {
            try
            {
                SeriesChartType selectedChartType = ((IdValueMember<SeriesChartType, string>)cmbChartType.SelectedItem).Id; // RKD
                chrtSpaceAnalysis.Series.ToList().ForEach(srs => srs.ChartType = selectedChartType);
                LastChartType = selectedChartType;
            }
            catch (Exception)
            {
                chrtSpaceAnalysis.Series.ToList().ForEach(srs => srs.ChartType = LastChartType);
            }
        }
        #endregion

        #region Methods
        private void UpdateColor()
        {
            btnMinMax.BackColor = chkMax.Checked ? Color.Green : Color.Red;
        }

        private void UpdateStatusBox()
        {
            var hScrollbar = dgrdDirectoryInfo.Controls.OfType<HScrollBar>().First();
            var vScrollbar = dgrdDirectoryInfo.Controls.OfType<VScrollBar>().First();

            switch (chkMax.CheckState)
            {
                case CheckState.Checked:
                    double fractionHeight = 2.0 / 5;
                    double fractionWidth = 1.0 / 2;

                    int lstItemCount = Convert.ToInt32(((dgrdDirectoryInfo.Height * fractionHeight - 30) - 4) / lstStatus.ItemHeight);
                    int lstHeight = lstStatus.ItemHeight * lstItemCount + 4;

                    lstStatus.Visible = lblStatus.Visible = true;
                    pnlStatus.Size = new Size(Convert.ToInt32(dgrdDirectoryInfo.Width * fractionWidth), lstHeight + 30);
                    pnlStatus.Location = new Point(
                        dgrdDirectoryInfo.Location.X + dgrdDirectoryInfo.Width - (pnlStatus.Width + (vScrollbar.Visible ? 18 : 0)),
                        dgrdDirectoryInfo.Location.Y + dgrdDirectoryInfo.Height - (pnlStatus.Height + (hScrollbar.Visible ? 18 : 0)));
                    break;

                case CheckState.Unchecked:
                    lstStatus.Visible = lblStatus.Visible = false;
                    pnlStatus.Size = new Size(30, 30);
                    pnlStatus.Location = new Point(
                        dgrdDirectoryInfo.Location.X + dgrdDirectoryInfo.Width - (pnlStatus.Width + 2),
                        dgrdDirectoryInfo.Location.Y + dgrdDirectoryInfo.Height - (pnlStatus.Height + 1));
                    break;

                case CheckState.Indeterminate:
                default:
                    break;
            }
        }

        private string SelectData(DataGridViewSelectedRowCollection selectedRows, bool copyHeaders = false)
        {
            string selectedData = "";
            if (copyHeaders)
            {
                foreach (DataGridViewColumn dcDirInfo in dgrdDirectoryInfo.Columns)
                    selectedData += dcDirInfo.HeaderText + "\t";

                selectedData = selectedData.Trim('\t') + "\n";
            }

            foreach (DataGridViewRow drDirInfo
                in selectedRows.Cast<DataGridViewRow>().OrderBy(val => val.Index))
            {
                foreach (DataGridViewColumn dcDirInfo in dgrdDirectoryInfo.Columns)
                    selectedData += drDirInfo.Cells[dcDirInfo.Name].Value + "\t";

                selectedData = selectedData.Trim('\t') + "\n";
            }

            return selectedData;
        }

        private void InitializeControlData()
        {
            ClearGrid();
            pnlChart.Visible = false;
            tglLoadChart.UpdateCheckState(false);
            UpdateStatusBox();

            InitDGAndUpdateSortOptions();
        }
        #endregion

        private void btnEditColumn_Click(object sender, EventArgs e)
        {

        }

        // RKD - pending
        private void cmnuSaveChartAsImage_Click(object sender, EventArgs e)
        {
            //sender.name > JPG/cmnuSaveAsPNG/BMP
            ScreenshotUtil.TakeScreenshot(this);
        }

        // RKD - pending
        private void cmnuSaveReportToFile(object sender, EventArgs e)
        {
            //sender.name > XLS/PDF
        }

        private void frmDirectoryInfo_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!supressCloseConfirmation)
                FormClosingEvent._FormClosing(sender, e, ref supressCloseConfirmation, this);
        }

        private void btnClearPath_Click(object sender, EventArgs e)
        {
            txtPath.Clear();
            if (Form.ModifierKeys != Keys.Control) ClearGrid();

            Notifications.PlaySound(eNotificationSound.Cleanup, Program.IsMute);
        }

        private void ClearGrid()
        {
            if (dgrdDirectoryInfo.Rows.Count > 0)
                UpdateStatus(ResourceDirectoryAnalyzer.Status_ClearingGrid);

            switch (AnalysisMode)
            {
                case eAnalysisMode.FileType:
                    PopulateGrid(new ObservableCollection<FileCategory>());
                    break;

                case eAnalysisMode.SubDirectory:
                default:
                    PopulateGrid(new ObservableCollection<DInfo>());
                    break;
            }
        }

        private void dgrdDirectoryInfo_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex == -1 || e.ColumnIndex == -1) return;

            DataGridView dgView = (DataGridView)sender;
            if (dgView.Columns[e.ColumnIndex].CellType == (typeof(DataGridViewLinkCell)))
            {
                DataGridViewCell dgCell = dgView.Rows[e.RowIndex].Cells[e.ColumnIndex];
                string path = sBasePath.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar
                    + dgCell.Value.Coalesce<string>().ToString().Trim('.');

                if (!Directory.Exists(path)) path = txtPath.Text;
                switch (AnalysisMode)
                {
                    case eAnalysisMode.SubDirectory:
                        if (Form.ModifierKeys == Keys.Control)
                        {
                            if (path.Trim(Path.DirectorySeparatorChar) != txtPath.Text.Trim(Path.DirectorySeparatorChar))
                            {
                                if (MessageBoxUtil.Show(Debugger.IsAttached,
                                    ResourceDirectoryAnalyzer.Msg_AnalyseSelectedPath.FormatTextFromParams(path),
                                    ResourceDirectoryAnalyzer.App_Confirmation,
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                                {
                                    txtPath.Text = path;
                                    btnLoad_Click(sender, e);
                                }
                            }
                        }
                        else Utilities.OpenPath(path);
                        break;

                    case eAnalysisMode.FileType:
                        var fileType = dgCell.Value.Coalesce<string>().ToString().Trim('.');
                        var allFilePaths = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToObservableCollection();

                        var extensions = FileTypes.AllFileTypes.GetValueFromKey(fileType);
                        if (extensions == null) MessageBox.Show("Not allowed for " + fileType);
                        else
                        {
                            allFilePaths = allFilePaths.Where(fl => extensions.Contains(Path.GetExtension(fl).Trim('.').ToUpper())).ToObservableCollection();
                            // new frmFilesFromType(fileType, allFilePaths).Show(); // RKD
                        }
                        break;

                    default:
                        throw new NotImplementedException("Action for AnalysisMode not defined: " + AnalysisMode.ToString());
                }


            }
        }

        private void dgrdDirectoryInfo_DataSourceChanged(object sender, EventArgs e)
        {
            DataGridView dgView = (DataGridView)sender;
            DataGridViewColumn dcView = null;

            foreach (DataGridViewColumn dc in dgView.Columns)
            {
                if (dc.CellType == (typeof(DataGridViewLinkCell)))
                {
                    dcView = dc;
                    break;
                }
            }

            if (!dcView.IsNull())
            {
                foreach (DataGridViewRow dr in dgView.Rows)
                {
                    if (dgView.Rows.Count > 2)
                    {
                        if (dr != dgView.Rows[0] && dr != dgView.Rows[dgView.Rows.Count - 1])
                        {
                            DataGridViewCell dgCell = dr.Cells[dcView.Name];
                            dgCell.ToolTipText = "Path:     ~\\" + dgCell.Value.Coalesce<string>()
                                + "\nClick to open; Ctrl+Click to analyze..";
                        }
                    }
                }
            }
        }

        private void cmbSortProperty_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbSortProperty.Enabled)
            {
                tltipSpaceAnalyzer.SetToolTip(cmbSortProperty, cmbSortProperty.Text);

                if (AnalysisHistory.GetValueFromKey(sBasePath).IsNull()) ClearGrid();
                else PopulateGrid(AnalysisHistory.GetValueFromKey(sBasePath).GetValueFromKey(AnalysisMode));
            }
            else
                tltipSpaceAnalyzer.Hide(this);
        }

        private void PopulateGrid(IEnumerable<object> data)
        {
            // Sorting
            btnSortDirectioner.UpdateSortText();

            // Populate data
            switch (AnalysisMode)
            {
                case eAnalysisMode.FileType:
                    dgrdDirectoryInfo.DataSource = data._Cast<FileCategory>()
                        ._OrderBy(dir => dir.Sort).ThenByProperty(cmbSortProperty.SelectedValue.Coalesce<string>("Sort"), btnSortDirectioner.IsAscending)
                        .ToObservableCollection();
                    break;

                case eAnalysisMode.SubDirectory:
                default:
                    dgrdDirectoryInfo.DataSource = data._Cast<DInfo>()
                        ._OrderBy(dir => dir.Sort).ThenByProperty(cmbSortProperty.SelectedValue.Coalesce<string>("Sort"), btnSortDirectioner.IsAscending)
                        .ToObservableCollection();
                    break;
            }

            // Highlight Total Size
            if (dgrdDirectoryInfo.Rows.Count > 0)
            {
                int lastIndex = dgrdDirectoryInfo.Rows.Count - 1;
                DataGridViewCellStyle cellStyle = dgrdDirectoryInfo.Rows[lastIndex].DefaultCellStyle;
                DataGridViewCellStyle headerStyle = dgrdDirectoryInfo.RowHeadersDefaultCellStyle;
                Font fnt = headerStyle.Font;
                cellStyle.Font = new Font(fnt.Name, 9F, FontStyle.Bold);
                cellStyle.ForeColor = Color.White;
                cellStyle.BackColor = Color.Black;
                cellStyle.SelectionForeColor = Color.Purple;
                cellStyle.SelectionBackColor = Color.DarkGray;
                cellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                dgrdDirectoryInfo[0, lastIndex].Style.ForeColor = cellStyle.ForeColor;
                dgrdDirectoryInfo[0, lastIndex].Style.BackColor = Color.DarkGray;
                //dgrdDirectoryInfo.Rows[lastIndex].Cells[0].RowSpan = 3; // RKD

                /* RKD :: HELP (merge 3 cells of last row as "Total Size")
                 * http://www.codeproject.com/Questions/134970/How-to-merge-datagridview-cells
                 * http://marss.co.ua/MergingCellsInGridView.aspx
                 * http://www.codeproject.com/Questions/624774/datagridview-header-cells-merge
                */
            }
        }

        private void radDisplayOption_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rad = (RadioButton)sender;
            if (rad.Checked)
            {
                UpdateMode(false);
                InitDGAndUpdateSortOptions();

                ValidateFileTypeCheckbox(rad);
                // RKD
                // if (rad.Name == radSubDirectories.Name)
                //     AnalysisMode = eAnalysisMode.FileType;
                // else if (rad.Name == radFileTypes.Name)
                //     AnalysisMode = eAnalysisMode.SubDirectory;
            }
        }

        private void ValidateFileTypeCheckbox(RadioButton rad)
        {
            chkUnknowFileTypes.Enabled = (rad.Name == radFileTypes.Name); // Enable Unknown File Types checkbox if sort by File Type is selected
        }

        private void UpdateMode(bool clearList)
        {
            // RKD : DELETE
            //switch (AnalysisMode)
            //{
            //    case eAnalysisMode.FileType:
            //        UpdateStatus(ResourceDirectoryAnalyzer.Status_Mode.FormatTextFromParams("File type"), true, clearList);
            //        break;
            //
            //    case eAnalysisMode.SubDirectory:
            //    default:
            //        UpdateStatus(ResourceDirectoryAnalyzer.Status_Mode.FormatTextFromParams("Sub-directories"), true, clearList);
            //        break;
            //}

            string desc = AnalysisMode.GetEnumDescription();
            UpdateStatus(ResourceDirectoryAnalyzer.Status_Mode.FormatTextFromParams(desc), true, clearList);
        }

        private void cmbPathHistory_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!IsUpdatingPathHistory & cmbPathHistory.Visible
                & cmbPathHistory.Text != sBasePath)
            {
                txtPath.Text = cmbPathHistory.Text;
            }
        }

        public bool IsUpdatingPathHistory { get; set; }

        private void PathHistoryDropDown_Show(object sender, EventArgs e)
        {
            if (cmbPathHistory.Items.Count > 0) cmbPathHistory.DroppedDown = true;
        }

        private void cmbPathHistory_Leave(object sender, EventArgs e)
        {
            cmbPathHistory.DroppedDown = false;
        }

        private void cmbPathHistory_DataSourceChanged(object sender, EventArgs e)
        {
            cmbPathHistory.DroppedDown = (cmbPathHistory.Items.Count > 0);
        }

        private void cmbPathHistory_DropDownClosed(object sender, EventArgs e)
        {
            //if (!cmbPathHistory.DroppedDown & txtPath.Focused) cmbPathHistory.DroppedDown = true;
        }

        private void WriteToActivityLog(IEnumerable<string> statusLog, DateTime startTime, DateTime completionTime, Exception ex, bool isWarning)
        {
            if (statusLog.IsLinqIterable(true))
            {
                string sLogBody = "";
                statusLog._ForEach(log => sLogBody += "\t" + log + Utilities.cNewLine);

                EventLogEntryType ev;
                if (!ex.IsNull()) ev = EventLogEntryType.Error;
                else ev = isWarning ? EventLogEntryType.Warning : EventLogEntryType.Information;

                Logger.WriteToLog(Program.Logger, sLogBody, startTime, completionTime, ex, ev);
            }
        }
    }
}
